package com.beantools.reflect.enums;

import com.beantools.constants.SingleChar;
import com.beantools.entity.enums.TailEnum;
import com.beantools.execption.PackageException;
import com.beantools.util.ArrayUtil;
import com.beantools.util.CollectionUtil;
import com.beantools.util.StringUtil;

import java.io.Serializable;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.beantools.constants.SingleChar.EN_SEMICOLON;
import static com.beantools.util.ArrayUtil.containNullObject;
import static com.beantools.util.ArrayUtil.removeNull;
import static com.beantools.util.CollectionUtil.*;
import static com.beantools.util.ObjectUtil.isNull;
import static com.beantools.util.StringUtil.isBlank;

/**
 * 这是自定义的类加载器，实现<code>Serializable</code>接口，用来加载指定包下的类信息。
 * <p>
 * 如果你设置了所读类的尾部过滤器，比如只读出以Enum结尾的枚举类；只读出以dao结尾的dao层的类；
 * <p>
 * 当然，你的命名必须按照标准命名法来命名，可以参考阿里巴巴的命名规则
 * <a href='https://yq.aliyun.com/articles/628692'>阿里巴巴规约</a>
 */

public class EnumClassLoader implements Serializable {

    private static final long serialVersionUID = 722867185880560068L;

    private static Logger logger = Logger.getLogger("");

    /**
     * 静态变量，当启动该类加载器时，打印出类加载的日志信息
     */
    static {
        logger.log(Level.INFO, "【beantools】类ClassUtil开始加载java包下文件");
    }


    /**
     * 这是过滤出多个包名的并以枚举类<code>TailEnum</code>作为约束条件的方法。
     * 如果枚举类<code>TailEnum</code>为空，则过滤指定包下的所有类
     *
     * @param packageNames 包名的集合，比如 com.beantools.reflect.enums.loader
     * @param tail         对象结尾识别，这是枚举类{@link TailEnum}
     * @return 返回指定包下的所有类，并装载到<code>Set</code>容器中
     */
    protected static Set<Class<?>> initialMultipleByEnum(TailEnum tail, String enumNames, String... packageNames) throws PackageException {
        if (ArrayUtil.isEmpty(packageNames)) {
            return null;
        }
        Set var = initialMultiplePackages(enumNames, packageNames);
        return isNull(tail) ? filterClass(var) : filterClass(var, tail.value());
    }

    /**
     * 过滤出指定包中的类，同时过滤出类的结尾所在枚举中的类调用者只想返回以Enum结尾的对象。
     * <p>
     * <code>
     * initialFilterSinglePackageByEnum("com.beantools.reflect.enums.loader",TAIL_ENUM );
     * </code>
     *
     * @param packageName 调用者所指定的包名
     * @param tails       对象结尾识别
     * @return 返回指定包下的所有类，并装载到<code>Set</code>容器中
     */
    protected static Set<Class<?>> initialFilterSinglePackageByEnum(String packageName, String enumNames, TailEnum... tails) {
        if (ArrayUtil.isEmpty(tails)) {
            return initialFilterSinglePackage(packageName, null);
        }
        String[] var = new String[tails.length];
        int index = 0;
        for (TailEnum tail : tails) {
            if (isNull(tail)) {
                continue;
            }
            var[index++] = tail.value();
        }
        String[] var1 = Arrays.copyOf(var, index);
        return initialFilterSinglePackage(packageName, enumNames, var1);
    }

    /**
     * 过滤出指定包中的类，同时过滤出类的结尾所在指定的识别字符串<code>tails</code>中的类。
     *
     * @param packageName 报名
     * @param tails       对象结尾识别字符串，比如“enum"，不区分大小写
     * @return 返回指定包下的所有类，并装载到<code>Set</code>容器中
     */
    public static Set<Class<?>> initialFilterSinglePackage(String packageName, String enumNames, String... tails) {

        Set<Class<?>> classes = initialSinglePackage(packageName, enumNames);
        if (isEmpty(classes)) {
            return null;
        }
        if (ArrayUtil.isEmpty(tails)) {
            return classes;
        }
        return filterClass(classes, tails);

    }


    /**
     * 获取指定多个包中的所有类的集合
     *
     * @param packageNames 指定包名集合
     * @return 返回该包下的所有类
     */
    protected static Set<Class<?>> initialMultiplePackages(String enumNames, String... packageNames) throws PackageException {
        // 将类信息保存到集合中存储
        if (ArrayUtil.isEmpty(packageNames)) {
            return null;
        }
        Set var = new LinkedHashSet();
        try {
            for (String packageName : packageNames) {
                Set<Class<?>> classes = initialSinglePackage(packageName, enumNames);
                var.addAll(classes);
            }
        } catch (PackageException e) {
            throw new PackageException(e.getMessage());
        }
        return var;
    }

    /**
     * 获取指定某个包下的所有类对象
     *
     * @param packageName 指定包名
     * @return 返回该包下的所有类
     */
    protected static Set<Class<?>> initialSinglePackage(String packageName, String enumNames) throws PackageException {
        // 将类信息保存到集合中存储
        Set<Class<?>> classSets = new LinkedHashSet<>();
        if (isBlank(packageName) || isBlank(enumNames)) {
            addClass(classSets, "com.beantools.entity.enums.NullObjectEnum");
            return classSets;
        }
        String[] var = enumNames.split(EN_SEMICOLON);
        String[] var1 = containNullObject(var) ? removeNull(var, String.class) : var;
        for (String s : var1) {
            String var2 = packageName + SingleChar.EN_POINT + s;
            addClass(classSets, var2);
        }
        if (CollectionUtil.isEmpty(classSets)){
            addClass(classSets, "com.beantools.entity.enums.NullObjectEnum");
        }
        return classSets;
    }

    /**
     * 将反射对象添加到集合中
     *
     * @param classSets 装载反射对象类的容器
     * @param className 类名，比如 com.beantools.reflect.enums.EnumClassLoader
     */
    private static void addClass(Set<Class<?>> classSets, String className) {
        try {
            Class<?> aClass = Class.forName(className);
            classSets.add(aClass);
        } catch (ClassNotFoundException e) {
            return;
        }
    }


    /**
     * 根据调用者输入的类结尾的字符串来过滤容器中的类
     *
     * @param classes 装载类的容器集合
     * @param tails   类结尾的字符串
     * @return 返回过滤后的类容器的对象
     */
    private static Set filterClass(Set<Class<?>> classes, String... tails) {
        Set var = new LinkedHashSet(classes.size());
        if (isEmpty(classes)) {
            return var;
        }
        for (Class<?> aClass : classes) {
            String var1 = aClass.getSimpleName();
            for (String tail : tails) {
                if (StringUtil.containsIgnoreCase(var1, tail)) {
                    var.add(aClass);
                    break;
                }
            }
        }
        return var;
    }
}
